package bll

import (
	"bytes"
	"fmt"
	"io"
	"signal/lib/trace_log"
	"signal/utils/goroutine_pool"
	"strings"
	"time"

	"github.com/panjf2000/gnet/v2"
	pkgUtils "gitlab.local/backend/pkg/utils"
	log "signal/collector/logger"
	"signal/errors"
	"signal/model"
	"signal/utils"
)

var Handler = &handler{}

// 处理TCP连接的handler，实现了gnet.EventHandler接口
type handler struct {
	//cache []byte
}

type RequestData struct {
	Packets []interface{}
	C       gnet.Conn
	Ip      string
}

type bitsReadType struct {
	startBytes    []byte
	length        uint
	bytesToObject func([]byte) interface{}
	checkCrc      func([]byte) bool
}

func (h *handler) init() func() {
	return func() {}
}

func (h *handler) OnBoot(e gnet.Engine) (action gnet.Action) {
	log.Debug("server start")
	return
}

func (h *handler) OnShutdown(e gnet.Engine) {
	log.Infof("server shutdown")
}

func (h *handler) OnOpen(c gnet.Conn) (out []byte, action gnet.Action) {
	trace_log.CommonLogger.Info("connId:", c.Fd(), " OnOpen 建立了一个新连接")
	Session.OnOpen(c)
	return nil, gnet.None
}

func (h *handler) OnClose(c gnet.Conn, err error) (action gnet.Action) {
	trace_log.CommonLogger.Info("connId:", c.Fd(), " OnClose 1 关闭了一个新连接")
	Session.OnClose(c, err)
	return gnet.None
}

func (h *handler) handlerLogin(d *RequestData) error {

	var (
		err         error
		resp        []byte
		c           = d.C
		packets     = d.Packets
		remoteIp    = c.RemoteAddr().String()
		firstPacket *model.Packet
		ok          bool
		step        byte
		errMsg      []string
		infoMsg     []string
	)

	defer func() {
		var (
			p       []byte
			logger  = log.Info
			message = infoMsg
			result  = "SUCCESS"
		)
		if len(errMsg) > 0 {
			logger = log.Error
			result = "FAILED"
			message = append(message, errMsg...)
		}

		if firstPacket != nil {
			p = firstPacket.ToHexBytes()
		}

		logger(fmt.Sprintf("【 handlerLogin remote ip:%+v,fd:%v,step %v,request:0x%X,response:0x%X, Result:%v 】 %v",
			remoteIp, c.Fd(), step, p, resp, result, strings.Join(message, " ")))
	}()

	if Session.IsLogged(c.Fd()) {
		return nil
	}

	if firstPacket, ok = packets[0].(*model.Packet); !ok {
		e := fmt.Errorf("first login packet format err, must start from %X", model.Magic)
		errMsg = append(errMsg, e.Error())
		return e
	}

	if model.CmdType(firstPacket.Cmd) != model.CmdLogin {
		e := fmt.Errorf("device is not login, but the first packet is not login packet")
		errMsg = append(errMsg, e.Error())
		_ = c.Close()
		return e
	}

	step = firstPacket.Body[0]
	if resp, err = Session.Login(c, firstPacket.Body); err != nil {
		e := fmt.Errorf("login error: %+v", err)
		errMsg = append(errMsg, e.Error())
		_ = c.Close()
		return e
	}

	if _, err = c.Write(resp); err != nil {
		e := fmt.Errorf("response err,write to socket[%v] error: %+v", c.Fd(), err)
		errMsg = append(errMsg, e.Error())
		return e
	}
	if err = c.Flush(); err != nil {
		e := fmt.Errorf("response err,flush to socket[%v] error: %+v", c.Fd(), err)
		errMsg = append(errMsg, e.Error())
		return e
	}

	infoMsg = append(infoMsg, fmt.Sprintf("response success, write to socket ok"))
	return nil
}

func (h *handler) OnTraffic(c gnet.Conn) (action gnet.Action) {
	var (
		n          int
		ip         = c.RemoteAddr().String()
		err        error
		buf        = make([]byte, 255)
		packets    []interface{}
		errPackets [][]byte
	)

	// read data from socket
	n, err = c.Read(buf)
	if err != nil {
		if err == io.EOF {
			return gnet.None
		}
		log.Errorf("read data from remote device：%v socket error: %s", ip, err)
		return gnet.None
	}
	log.Infof("【Receive】 data from remote device：%v local soket[%v],length:%v,packet:%X", ip, c.Fd(), len(buf[:n]), buf[:n])

	// parse protocol
	if packets, errPackets, err = h.Parse(c.Fd(), buf[:n]); err != nil {
		var es []string
		for _, v := range errPackets {
			es = append(es, fmt.Sprintf("%X", v))
		}
		log.Errorf("data is illegal, remote addr: %s,parse error bytes:[%v]", ip, strings.Join(es, ","))
		return gnet.None
	}

	if len(packets) == 0 {
		log.Errorf("receive empty data from remote addr: %s", ip)
		return gnet.None
	}

	// handle data
	goroutine_pool.GetPool().Push(&RequestData{
		Packets: packets,
		C:       c,
		Ip:      ip,
	}, func(data interface{}) error {

		var (
			requestData = data.(*RequestData)
			fd          = requestData.C.Fd()
			sn          = Session.GetSnByConnId(fd)
			remoteAddr  = fmt.Sprintf("%s://%s", c.RemoteAddr().Network(), c.RemoteAddr().String())
		)

		if err = h.handlerLogin(requestData); err != nil {
			return err
		}

		log.Infof("【Receive】 data from  sn: %s device: %X", sn, buf[:n])
		trace_log.DebugLogger(sn, "OnTraffic 消息上行 receive:", fmt.Sprintf("%X", buf[:n]), " len:", n, " connId:", c.Fd(), "sn:", sn, "from:", remoteAddr)
		Session.UpdateKeepalive(fd, time.Now().Unix()+model.DefaultTCPTimeout)
		// parse command and route to handler
		for _, v := range requestData.Packets {
			if v == nil {
				continue
			}

			if p, ok := v.(*model.Packet); ok {
				trace_log.DebugLogger(sn, fmt.Sprintf("OnTraffic 处理解析到的报文:%X", p.ToHexBytes()))
				switch p.Magic {
				// handle device request
				case 0xaa:
					// handle query response
					if p.ID != 0 {
						h.replyHandle(fd, p)
						continue
					}
					// handle device report
					if err = h.reportHandle(fd, p); err != nil {
						log.Errorf("cmdHandle command error: %s", err)
					}
				// handle command response
				case 0x7e:
					h.replyHandle(fd, p)
				}
			}

			if p, ok := v.(*PacketBSC22); ok {
				switch p.Magic {
				case 0x5aa5:
					if err = Report.OTAUpgradeBSC22(sn, p); err != nil {
						log.Errorf("cmdHandle command error: %s", err)
					}
					break
				}
			}
		}
		return nil
	})
	return gnet.None
}

func (h *handler) OnTick() (delay time.Duration, action gnet.Action) {
	return time.Second, gnet.None
}

func (h *handler) reportHandle(connId int, p *model.Packet) error {
	var (
		err error
		sn  = Session.GetSnByConnId(connId)
	)
	if sn == "" {
		log.Errorf("session not found, remote connId: %v", connId)
		return errors.New("session not found")
	}
	trace_log.DebugLogger(sn, fmt.Sprintf("reported %2X", p.Cmd))
	log.Infof("sn:%s reported %2X", sn, p.Cmd)
	switch p.Cmd {
	case model.CmdHeartBeat:
		// 心跳
		//err = Report.HeartBeat(sn, p.Body)
		// 回复相同的心跳
		h.replyHeartBeat(connId, p)
	case model.CmdFault:
		// 故障上报
		err = Report.Fault(sn, p.Body)
	case model.CmdAlarm:
		// 告警上报
		err = Report.Alarm(sn, p.Body)
	case model.CmdUpgrade:
		// 升级申请
		err = Report.OTAUpgrade(sn, p.Body)
	case model.CmdInfo0x81:
		err = Report.Location(sn, p.Body)
	case model.CmdInfo0x82:
		err = Report.ReportEveryMinute(sn, p.Body)
	case model.CmdInfo0x8A:
		err = Report.FullReport(sn, p.Body)
	}
	return err
}

// 应答处理
func (h *handler) replyHandle(connId int, requestPkt *model.Packet) {
	if sn := Session.GetSnByConnId(connId); sn != "" {
		requestKey := RequestKey(sn, model.CmdType(requestPkt.Cmd), requestPkt.ID)
		Session.Reply(requestKey, requestPkt)
		return
	}
	log.Errorf("session not found, remote connId: %v", connId)
}

// 应答心跳包
func (h *handler) replyHeartBeat(connId int, requestPkt *model.Packet) {
	if sn := Session.GetSnByConnId(connId); sn != "" {
		requestKey := RequestKey(sn, model.CmdType(requestPkt.Cmd), requestPkt.ID)
		if err := Session.ReplyHeartBeat(sn, requestKey, requestPkt); err == nil {
			log.Infof("sn:%s, reply heart beat succeed", sn)
		} else {
			log.Errorf("sn:%s, reply heart beat failed:%+v", sn, err)
		}
		return
	}
	log.Errorf("session not found, remote connId: %v", connId)
}

func (h *handler) parseWhichType(b []byte) *bitsReadType {
	var (
		bitsRead = []bitsReadType{
			{
				startBytes:    []byte{0xaa},
				length:        1,
				bytesToObject: model.BytesToObject,
				checkCrc:      model.CheckCrc,
			},
			{
				startBytes:    []byte{0x7e},
				length:        1,
				bytesToObject: model.BytesToObject,
				checkCrc:      model.CheckCrc,
			},
			{
				startBytes:    []byte{0x5a, 0xa5},
				length:        2,
				bytesToObject: BytesToObject,
				checkCrc:      CheckCrc,
			},
		}
		whichType *bitsReadType
	)

	/* 首先确定是哪一种数据报文 */
	for _, v := range bitsRead {

		var (
			reader    = utils.NewBitReader(b)
			headBytes = uint(len(v.startBytes))
		)
		readable, _ := reader.AvailBits()
		if readable == 0 {
			break
		}

		if r, _ := reader.AvailBits(); r >= headBytes*8 {
			if b, err := reader.ReadBytes(headBytes); err != nil {
				continue
			} else {

				bb := pkgUtils.NetToHostBytes(b, int(headBytes))
				if !bytes.Equal(bb, v.startBytes) {
					continue
				}

				whichType = &v
				break
			}
		}
	}
	return whichType
}

func (h *handler) Parse(fd int, origin []byte) ([]interface{}, [][]byte, error) {

	var (
		startPos     int
		readBytes    []byte
		errBody      [][]byte
		packets      = make([]interface{}, 0)
		sessionCache = Session.LoadCachePacket(fd)
		cache        = make([]byte, len(sessionCache))
	)

	/*
	   1. 读取上次的缓存的没有读取完的字节数
	*/
	copy(cache, sessionCache)
	sessionCache = sessionCache[:0]
	fullOrigin := append(cache, origin...)

	fmt.Printf("fullOrigin:%X\n", fullOrigin)
	for {
		var (
			reader    = utils.NewBitReader(fullOrigin[startPos:])
			whichType = h.parseWhichType(fullOrigin[startPos:]) // 1. 读取包头
		)

		/*
		   whichType == nil 表示包首部的若干字节不是报文头，需要一个字节一个字节跳过
		*/
		if whichType == nil {
			if readable, _ := reader.AvailBits(); readable == 0 {
				break
			}
			startPos = startPos + 1
			continue
		}

		readBytes = pkgUtils.NetToHostBytes(whichType.startBytes, len(whichType.startBytes))
		startPos = startPos + len(whichType.startBytes)
		_ = reader.SkipBits(uint(len(whichType.startBytes) * 8))
		var (
			b           []byte
			err         error
			remainBytes uint16
		)
		readable, _ := reader.AvailBits()
		if readable == 0 {
			sessionCache = append(readBytes, sessionCache...)
			break
		}

		/* 2. 读取包长度 */
		if r, _ := reader.AvailBits(); r < whichType.length*8 {
			if r > 0 {
				remind, _ := reader.ReadBytes(r / 8)
				sessionCache = append(sessionCache, remind...)
			}
			sessionCache = append(readBytes, sessionCache...)
			break
		}

		if b, err = reader.ReadBytes(whichType.length); err != nil {
			continue
		}

		/*3. 根据长度读取包剩余的字节数 */
		startPos = startPos + int(whichType.length)
		readBytes = append(readBytes, b...)
		remainBytes = uint16(utils.BytesToInt(b))
		if r, _ := reader.AvailBits(); r < uint(remainBytes)*8 {
			if r > 0 {
				remind, _ := reader.ReadBytes(r / 8)
				sessionCache = append(sessionCache, remind...)
			}
			sessionCache = append(readBytes, sessionCache...)
			break
		}

		if b, err = reader.ReadBytes(uint(remainBytes)); err != nil {
			continue
		}

		startPos = startPos + int(remainBytes)
		readBytes = append(readBytes, b...)
		/*
		   将字节数组转化为结构体对象
		*/
		if whichType.checkCrc(readBytes) {
			/* 检测 crc 错误,可能还不是一个完整的包,丢弃 */
			//startPos = len(readBytes)
			//if startPos > len(origin) {
			//	startPos = len(origin) - 1
			//}
			//continue
			packets = append(packets, whichType.bytesToObject(readBytes))
			readBytes = readBytes[:0]
		} else {
			errBody = append(errBody, readBytes)
		}
	}
	Session.SaveCachePacket(fd, sessionCache)
	return packets, errBody, nil
}
